﻿#include "QSerialPortModbus.h"

#include <QModbusRtuSerialMaster>
#include <QVariant>
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QEventLoop>
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
#include <QElapsedTimer>

QSerialPortModbus::QSerialPortModbus(QObject *parent):BaseModbus(parent)
{

}

void QSerialPortModbus::SetCommunicationParam(QString communicationParam)
{
    BaseCommunication::SetCommunicationParam(communicationParam);
    modbusDevice = new QModbusRtuSerialMaster();
    //{"PortName":"COM12","Parity":"No","BaudRate":38400,"DataBits":8,"StopBits":1};
    QJsonParseError  jsonError;
    QJsonDocument jsonDoc = QJsonDocument::fromJson(communicationParam.toUtf8(), &jsonError);
    if(!jsonDoc.isObject())
    {
        QString err=QString("communicationParam:%1 error!").arg(communicationParam);
        qCritical()<<__FUNCTION__<<__LINE__<<communicationParam;
        throw err;
    }
    QJsonObject jsonObject = jsonDoc.object();
    modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,jsonObject["PortName"].toString());
    modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,jsonObject["Parity"].toString());
    modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,jsonObject["BaudRate"].toInt());
    modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,jsonObject["DataBits"].toInt());
    modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,jsonObject["StopBits"].toInt());
}

bool QSerialPortModbus::Connect()
{
    modbusDevice->setTimeout(2000);
    modbusDevice->setNumberOfRetries(3);
    connect(modbusDevice, &QModbusClient::errorOccurred,this,&QSerialPortModbus::OnError1);
    return modbusDevice->connectDevice();;
}

bool QSerialPortModbus::IsConnected()
{
    return modbusDevice->state()==QModbusDevice::ConnectedState;
}

void QSerialPortModbus::Dispose()
{
    modbusDevice->disconnectDevice();
}

bool QSerialPortModbus::ReadOutRegisters(int addr, int count, QList<uint16_t> &retList)
{
    return ReadRegistersImp(QModbusDataUnit::RegisterType::HoldingRegisters,addr,count,retList);
}

bool QSerialPortModbus::ReadInRegisters(int addr, int count, QList<uint16_t> &retList)
{
    return ReadRegistersImp(QModbusDataUnit::RegisterType::InputRegisters,addr,count,retList);
}

bool QSerialPortModbus::ReadOutBits(int addr, int count, QList<bool> &retList)
{
    QList<uint16_t> tempList;
    bool ret=ReadRegistersImp(QModbusDataUnit::Coils,addr,count,tempList);
    if(ret)
    {
        for(auto item:tempList)
        {
            retList.append(item);
        }
    }
    return ret;
}

bool QSerialPortModbus::ReadInBits(int addr, int count, QList<bool> &retList)
{
    QList<uint16_t> tempList;
    bool ret=ReadRegistersImp(QModbusDataUnit::DiscreteInputs,addr,count,tempList);
    if(ret)
    {
        for(auto item:tempList)
        {
            retList.append(item);
        }
    }
    return ret;
}

bool QSerialPortModbus::WriteRegisters(int addr, int count, QList<uint16_t> inputList)
{
    QModbusDataUnit unit(QModbusDataUnit::RegisterType::HoldingRegisters, addr, count);
    for(int i=0;i<count;i++)
    {
        unit.setValue(i,inputList.at(i));
    }
    QModbusReply *reply=modbusDevice->sendWriteRequest(unit,1);
    if(reply==nullptr)
    {
        throw QString("Modbus 写入寄存器失败,请检查连接!");
    }

    QEventLoop loop;
    QObject::connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
    loop.exec();
    bool ret=reply->error()==QModbusDevice::NoError;
    reply->deleteLater();
    return ret;
}

bool QSerialPortModbus::WriteBits(int addr, int count, QList<bool> inputList)
{
    QModbusDataUnit unit(QModbusDataUnit::RegisterType::Coils, addr, count);
    for(int i=0;i<count;i++)
    {
        unit.setValue(i,inputList.at(i));
    }
    QModbusReply *reply=modbusDevice->sendWriteRequest(unit,1);
    if(reply==nullptr)
    {
        throw QString("Modbus 写入bit失败,请检查连接!");
    }

    QEventLoop loop;
    QObject::connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
    loop.exec();
    bool ret=reply->error()==QModbusDevice::NoError;
    reply->deleteLater();
    return ret;
}

bool QSerialPortModbus::ReadRegistersImp(QModbusDataUnit::RegisterType registerType, int addr, int count, QList<uint16_t> &retList)
{
    QModbusDataUnit unit(registerType, addr, count);

    QModbusReply *reply=modbusDevice->sendReadRequest(unit,1);
    if(reply==nullptr)
    {
        throw QString("Modbus 写入数据失败,请检查连接!");
    }
    QEventLoop loop;
    QObject::connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
    QObject::connect(reply, &QModbusReply::errorOccurred, &loop, &QEventLoop::quit);
    loop.exec();

    if(reply->error()==QModbusDevice::NoError)
    {
        const QModbusDataUnit responseUnit=reply->result();
        for(uint i=0;i<responseUnit.valueCount();i++)
        {
            retList.append(responseUnit.value(i));
        }
        reply->deleteLater();
        return true;
    }
    reply->deleteLater();
    return false;
}

void QSerialPortModbus::CreateModbusInstance()
{
}

void QSerialPortModbus::OnError1(QModbusDevice::Error error)
{
    qDebug()<<__FUNCTION__<<_name<<"modbus error"<<error<<modbusDevice->errorString();
}
